commonlibsse_ng\re\b/
ButtonEvent.rs1use core::alloc::Layout;
2use core::ptr;
3
4use crate::re::BSFixedString::BSFixedString;
5use crate::re::IDEvent::{IDEvent, IDEventVtbl};
6use crate::re::InputDevices::INPUT_DEVICE;
7use crate::re::InputEvent::{INPUT_EVENT_TYPE, InputEvent};
8use crate::re::MemoryManager::alloc::alloc_zeroed;
9use crate::re::TESBox::TESBox;
10use crate::re::offsets_rtti::RTTI_ButtonEvent;
11use crate::re::offsets_vtable::VTABLE_ButtonEvent;
12use crate::rel::ResolvableAddress;
13use crate::rel::id::{DataBaseError, VariantID};
14use crate::rel::module::is_vr;
15use crate::rel::relocation::{RelocationError, relocate_member, relocate_member_mut};
16
17#[repr(C)]
18#[derive(Debug, PartialEq)]
19pub struct ButtonEvent {
20 pub __base: IDEvent, }
22const _: () = assert!(core::mem::size_of::<ButtonEvent>() == 0x28);
23
24impl ButtonEvent {
25 pub const RTTI: VariantID = RTTI_ButtonEvent;
27
28 pub const VTABLE: [VariantID; 1] = VTABLE_ButtonEvent;
30
31 pub fn vtable() -> Result<&'static ButtonEventVtbl, DataBaseError> {
33 Self::VTABLE[0].address().map(|vtable| unsafe { vtable.cast().as_ref() })
34 }
35
36 #[allow(clippy::unwrap_in_result)]
40 pub fn new_boxed(
41 input_device: INPUT_DEVICE,
42 user_event: BSFixedString,
43 id_code: u32,
44 value: f32,
45 held_down_secs: f32,
46 ) -> Result<TESBox<Self>, DataBaseError> {
47 const VR_BUTTON_EVENT_SIZE: usize = 0x30;
48 const TOTAL_SIZE: usize = VR_BUTTON_EVENT_SIZE + size_of::<RUNTIME_DATA>();
49
50 let layout = Layout::from_size_align(TOTAL_SIZE, align_of::<Self>()).expect("Valid layout");
51
52 unsafe {
53 let ptr = alloc_zeroed(layout).cast::<u8>();
54 if ptr.is_null() {
55 #[cfg(feature = "tracing")]
56 tracing::error!("Heap allocation failed");
57 return Err(DataBaseError::Poisoned);
58 }
59
60 let vtable = Self::vtable()? as *const ButtonEventVtbl;
62
63 let event_ptr = ptr.cast::<Self>();
65 ptr::write(
66 event_ptr,
67 Self {
68 __base: IDEvent {
69 __base: InputEvent {
70 vtable: vtable.cast(),
71 device: input_device,
72 eventType: INPUT_EVENT_TYPE::Button,
73 next: None,
74 },
75 userEvent: user_event,
76 idCode: id_code,
77 pad24: 0,
78 },
79 },
80 );
81
82 {
84 const SE_BUTTON_EVENT_SIZE: usize = 0x28;
85 let runtime_offset =
86 if is_vr() { SE_BUTTON_EVENT_SIZE } else { VR_BUTTON_EVENT_SIZE };
87 let runtime_ptr = ptr.add(runtime_offset).cast::<RUNTIME_DATA>();
88 ptr::write(runtime_ptr, RUNTIME_DATA { value, heldDownSecs: held_down_secs });
89 }
90
91 Ok(TESBox::from_raw(event_ptr))
92 }
93 }
94
95 #[inline]
101 pub fn get_runtime_data(&self) -> Result<&RUNTIME_DATA, RelocationError> {
102 relocate_member(self, 0x28, 0x30)
103 }
104
105 #[inline]
111 pub fn get_runtime_data_mut(&mut self) -> Result<&mut RUNTIME_DATA, RelocationError> {
112 relocate_member_mut(self, 0x28, 0x30)
113 }
114
115 #[inline]
116 pub fn value(&self) -> Option<f32> {
117 Some(self.get_runtime_data().ok()?.value)
118 }
119
120 #[inline]
121 pub fn held_duration(&self) -> Option<f32> {
122 Some(self.get_runtime_data().ok()?.heldDownSecs)
123 }
124
125 #[inline]
126 pub fn is_pressed(&self) -> bool {
127 match self.value() {
128 Some(value) => value > 0.0,
129 None => false,
130 }
131 }
132
133 #[inline]
134 pub fn is_repeating(&self) -> bool {
135 match self.held_duration() {
136 Some(value) => value > 0.0,
137 None => false,
138 }
139 }
140
141 #[inline]
142 pub fn is_down(&self) -> bool {
143 self.is_pressed() && self.held_duration().is_some_and(|duration| duration > 0.0)
144 }
145
146 #[inline]
147 pub fn is_held(&self) -> bool {
148 self.is_pressed() && self.is_repeating()
149 }
150
151 #[inline]
152 pub fn is_up(&self) -> bool {
153 self.value().is_some_and(|value| value > 0.0) && self.is_repeating()
154 }
155}
156
157pub struct ButtonEventVtbl {
158 pub __base: IDEventVtbl, }
160
161#[derive(Debug, Clone, Default, PartialEq)]
162pub struct RUNTIME_DATA {
163 pub value: f32,
164 pub heldDownSecs: f32,
165}
166const _: () = assert!(core::mem::size_of::<RUNTIME_DATA>() == 0x8);